package me.leon.lib4x.log

import android.text.TextUtils
import android.util.Log
import java.io.File
import java.io.PrintWriter
import java.io.RandomAccessFile
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.*

object Leon {
    private const val DEFAULT_TAG = "LeonLogger"
    private var FILE_PATH = "/sdcard/data/Leon/log.txt"
    private var FILE_MAX_LENGTH = 10485760
    private var DEBUG = true
    private var IS_PRINT = true
    private var IS_SAVE_FILE = true
    private const val V = Log.VERBOSE
    private const val D = Log.DEBUG
    private const val I = Log.INFO
    private const val W = Log.WARN
    private const val E = Log.ERROR

    private val curTime: String
        get() {
            val format = SimpleDateFormat("yy-MM-dd HH:mm:ss.sss")
            val date = Date(System.currentTimeMillis())
            return format.format(date)
        }

    fun generateTag(): String {
        var caller = Throwable().stackTrace[2]
        var tag = "at %s.%s(%s:%d)"
        var fullClazzName = caller.className
        var callerClazzName = fullClazzName.substring(fullClazzName.lastIndexOf(".") + 1)
        tag = String.format(tag, fullClazzName, caller.methodName, caller.fileName, caller.lineNumber)

        tag = if (TextUtils.isEmpty(DEFAULT_TAG)) tag else "$DEFAULT_TAG:$tag"
        return tag
    }

    fun i(msg: String) {
        log(I, DEFAULT_TAG, msg, null)
    }

    fun i(tag: String, msg: String) {
        log(I, tag, msg, null)
    }

    fun ll(tag: String, msg: String) {
        log(I, generateTag(), msg, null)
    }

    private fun log(type: Int, tag: String, msg: String, thr: Throwable?) {
        if (DEBUG) {
            if (IS_PRINT) {
                when (type) {
                    V -> Log.v(tag, String.format("[DEBUG] %s", msg))
                    D -> Log.d(tag, String.format("[DEBUG] %s", msg))
                    I -> Log.i(tag, String.format("[DEBUG] %s", msg))
                    W -> Log.w(tag, String.format("[DEBUG] %s", msg))
                    E -> Log.e(tag, String.format("[DEBUG] %s", msg))
                }
            }

            if (IS_SAVE_FILE) {
                writeLogAppend(tag, msg)
                if (thr != null) {
                    writeLogAppend(tag, getErrorInfo(thr))
                }
            }
        }
    }

    fun i(tag: String, msg: String, thr: Throwable) {
        log(I, tag, msg, thr)
    }

    fun v(msg: String) {
        log(V, DEFAULT_TAG, msg, null)
    }

    fun v(tag: String, msg: String) {
        log(V, tag, msg, null)

    }

    fun v(tag: String, msg: String, thr: Throwable) {
        log(V, tag, msg, thr)
    }

    fun e(msg: String) {
        log(E, DEFAULT_TAG, msg, null)

    }

    fun e(tag: String, msg: String) {
        log(E, tag, msg, null)

    }

    fun e(tag: String, msg: String, thr: Throwable) {
        log(E, tag, msg, thr)

    }

    fun d(msg: String) {
        log(D, DEFAULT_TAG, msg, null)

    }

    fun d(tag: String, msg: String) {
        log(D, tag, msg, null)

    }

    fun d(tag: String, msg: String, thr: Throwable) {
        log(D, tag, msg, thr)

    }

    fun w(tag: String, msg: String) {
        log(W, tag, msg, null)

    }

    fun w(msg: String) {
        log(W, DEFAULT_TAG, msg, null)

    }

    fun w(tag: String, msg: String, thr: Throwable) {
        log(W, tag, msg, thr)

    }

    fun setDebug(debug: Boolean) {
        DEBUG = debug
    }

    fun setPrint(print: Boolean) {
        IS_PRINT = print
    }

    fun setPrintFile(printFile: Boolean) {
        IS_SAVE_FILE = printFile
    }

    fun setLogFilePath(savePath: String) {
        FILE_PATH = savePath
    }

    fun setMaxLogFileLength(max: Int) {
        FILE_MAX_LENGTH = max
    }

    private fun getErrorInfo(throwable: Throwable): String {
        val writer = StringWriter()
        val localPrintWriter = PrintWriter(writer)
        throwable.printStackTrace(localPrintWriter)
        localPrintWriter.close()
        return writer.toString()
    }

    private fun writeLogAppend(tag: String, msg: String) {
        val file = File(FILE_PATH)
        if (!file.exists()) {
            val directory = FILE_PATH.substring(0, FILE_PATH.lastIndexOf("/"))
            val dirFile = File(directory)
            if (!dirFile.exists()) {
                dirFile.mkdirs()
            }

            try {
                file.createNewFile()
            } catch (e: Exception) {
                e.printStackTrace()
            }

        }

        try {
            val randomFile = RandomAccessFile(FILE_PATH, "rw")
            var fileLength = randomFile.length()
            if (fileLength > FILE_MAX_LENGTH.toLong()) {
                randomFile.setLength(0L)
                fileLength = 0L
            }

            randomFile.seek(fileLength)
            randomFile.write(String.format("\n%s %s %s", curTime, tag, msg).toByteArray(charset("gb2312")))
            randomFile.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }
}
